﻿using Aliyun.OSS;
using Aliyun.OSS.Common;
using BCGM.MedicalWisdom.Model.ViewModels.Base;
using Microsoft.AspNetCore.Http;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BCGM.MedicalWisdom.Common.AliYun
{
    public class OSSHelper
    {
        #region Fields
        //https://zww1.oss-cn-shenzhen.aliyuncs.com/xxxxxx/filebase_dev/oringinal/123456dfsf.png

        private static OssClient _ossClient;

        private static AutoResetEvent _event;

        //账号Id
        private const string AccessKeyId = "xxxxxxxxxx";

        //账号key
        private const string AccessKeySecret = "xxxxxxxxxx";

        //oss所在的服务区域
        private const string Endpoint = "https://oss-cn-shenzhen.aliyuncs.com";

        //生成的图片的路径地址
        private const string Branch = @"xxxxxx/filebase_dev/";

        // 填写Bucket名称。
        private const string BucketName = "zww1";

        // 填写Object完整路径。Object完整路径中不能包含Bucket名称。
        private const string objectName = "/xxxxxx/filebase_dev/";

        // 填写字符串。
        private const string objectContent = "";

        private const string ThumbSize = "350";

        private const string DefaultSize = "750";
        #endregion

        #region Constructors

        private OSSHelper()
        {
            _event = new AutoResetEvent(false);
            _ossClient = new OssClient(Endpoint, AccessKeyId, AccessKeySecret);
        }

        public static OSSHelper _()
        {
            return new OSSHelper();
        }

        #endregion

        #region Method

        /// <summary>
        /// 通过文件流普通上传 10 普通上传  20 断点续传上传  30进度条上传
        /// </summary>
        /// <returns></returns>
        public async Task<FileBaseRequest> HttpUpload(IFormFile file, int uploadtype = 10)
        {
            string nameStr = string.Empty;
            string name = file.FileName;
            if (!string.IsNullOrEmpty(name) && name.Contains('.'))
            {
                nameStr = name.Split('.')[0];
            }
            string type = file.ContentType;
            string guid = Guid.NewGuid().ToString();
            string filebaseUrl = string.Empty;
            string hash = "";
            long? size = 0;

            int? width = 0;
            int? height = 0;
            int? shapeTypeId = 0;

            var strArr = type.Split('/');

            using (var memoryStream = new MemoryStream())
            {
                await file.CopyToAsync(memoryStream);
                size = file.Length;
                hash = ComputeMd5Hash(memoryStream);

                //if (strArr[0].ToString() == "image")
                //{
                //    using (Bitmap pic = new Bitmap(memoryStream))
                //    {
                //        width = pic.Size.Width;
                //        height = pic.Size.Height;
                //        if (width > height)
                //            shapeTypeId = (int)ShapeType.Sideways;
                //        if (width < height)
                //            shapeTypeId = (int)ShapeType.vertical;
                //        if (width == height)
                //            shapeTypeId = (int)ShapeType.Square;
                //    }
                //}

                if (strArr[0].ToString() == "video")
                {

                }

                byte[] oringinalBuffer, thumbBuffer, defaultBuffer;
                oringinalBuffer = memoryStream.ToArray();
                thumbBuffer = GetThumbnail(oringinalBuffer, int.Parse(ThumbSize));
                defaultBuffer = GetThumbnail(oringinalBuffer, int.Parse(DefaultSize));

                switch (uploadtype)
                {
                    case 10:
                        using (MemoryStream requestContent = new MemoryStream(oringinalBuffer))
                        {
                            filebaseUrl = $"{Branch}oringinal/{guid}.jpg";
                            _ossClient.PutObject(BucketName, filebaseUrl, requestContent);
                        }
                        using (MemoryStream requestContent = new MemoryStream(thumbBuffer))
                        {
                            _ossClient.PutObject(BucketName, $"{Branch}thumb/{guid}.jpg", requestContent);
                        }

                        using (MemoryStream requestContent = new MemoryStream(defaultBuffer))
                        {
                            _ossClient.PutObject(BucketName, $"{Branch}default/{guid}.jpg", requestContent);
                        }
                        break;

                    case 20:
                        using (MemoryStream requestContent = new MemoryStream(oringinalBuffer))
                        {
                            filebaseUrl = $"{Branch}oringinal/{guid}.jpg";
                            var result = BreakPoint(BucketName, filebaseUrl, requestContent, nameStr);
                        }
                        using (MemoryStream requestContent = new MemoryStream(thumbBuffer))
                        {
                            var result = BreakPoint(BucketName, $"{Branch}thumb/{guid}.jpg", requestContent, nameStr);
                        }
                        using (MemoryStream requestContent = new MemoryStream(defaultBuffer))
                        {
                            var result = BreakPoint(BucketName, $"{Branch}default/{guid}.jpg", requestContent, nameStr);
                        }
                        break;

                    case 30:
                        using (MemoryStream requestContent = new MemoryStream(oringinalBuffer))
                        {
                            filebaseUrl = $"{Branch}oringinal/{guid}.jpg";
                            var result = PutObjectProgress(BucketName, filebaseUrl, requestContent);
                        }
                        using (MemoryStream requestContent = new MemoryStream(thumbBuffer))
                        {
                            var result = PutObjectProgress(BucketName, $"{Branch}thumb/{guid}.jpg", requestContent);
                        }
                        using (MemoryStream requestContent = new MemoryStream(defaultBuffer))
                        {
                            var result = PutObjectProgress(BucketName, $"{Branch}default/{guid}.jpg", requestContent);
                        }
                        break;

                    default:
                        break;
                }
            }
            return new FileBaseRequest
            {
                FilebaseUrl = $"https://zww1.oss-cn-shenzhen.aliyuncs.com/{filebaseUrl}",
                Guid = guid,
                Hash = hash,
                Height = height,
                Name = name,
                ShapeTypeId = (int)shapeTypeId,
                Size = size,
                Type = type,
                Width = width,
            };
        }


        /// <summary>
        /// 单条下载
        /// </summary>
        /// <returns></returns>
        public async Task<byte[]> HttpDownload(string url)
        {
            try
            {
                RestClient restClient = new RestClient();
                var request = new RestRequest(url);
                var byteFile = restClient.DownloadData(request);
                return byteFile;
            }
            catch (Exception ex)
            {
                throw new AggregateException(ex.Message);
            }
        }


        /// <summary>
        /// 单条下载文件并转流
        /// </summary>
        /// <param name="fileName">文件路径</param>
        /// <param name="isDelete">是否删除临时文件</param>
        /// <returns></returns>
        public Stream FileToStream(string fileName, bool isDelete = false, string url = null)
        {
            using (var client = new WebClient())
            {
                string tempFile = Path.GetTempFileName();
                client.DownloadFile(url, tempFile);//下载临时文件
                                                   //Console.WriteLine("Using " + tempFile);

                //打开文件
                FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);

                // 读取文件的 byte[]
                byte[] bytes = new byte[fileStream.Length];

                fileStream.Read(bytes, 0, bytes.Length);

                fileStream.Close();

                // 把 byte[] 转换成 Stream
                Stream stream = new MemoryStream(bytes);
                if (isDelete)
                {
                    File.Delete(fileName);//删除临时文件
                }
                return stream;
            }
        }


        /// <summary>
        /// 通过文件列表的Url地址下载文件
        /// </summary>
        /// <param name="url">下载文件地址</param>
        /// <returns></returns>
        public async Task<Stream> HttpDownloadList(List<DownFileInfo> downFileInfos)
        {
            byte[] buffer = new byte[] { };

            foreach (var item in downFileInfos)
            {
                RestClient restClient = new RestClient();
                var request = new RestRequest(item.Url);
                buffer = restClient.DownloadData(request);
            }
            return new MemoryStream(buffer);
        }
        #endregion


        #region Utilities
        /// <summary>
        /// 进度条上传方法
        /// </summary>
        /// <param name="BucketName"></param>
        /// <param name="objectName"></param>
        /// <param name="stream"></param>
        /// <param name="CheckpointDir"></param>
        /// <returns></returns>
        public async Task<bool> PutObjectProgress(string bucketName, string objectName, Stream stream)
        {
            bool state = true;
            try
            {
                var putObjectRequest = new PutObjectRequest(bucketName, objectName, stream);
                putObjectRequest.StreamTransferProgress += streamProgressCallback;
                _ossClient.PutObject(putObjectRequest);
            }
            catch (OssException ex)
            {
                throw new AggregateException($"Failed with error code: {ex.ErrorCode}; Error info: {ex.Message}. \nRequestID: {ex.RequestId}\tHostID: {ex.HostId}");
            }
            catch (Exception ex)
            {
                throw new AggregateException(ex.Message);
            }
            return state;
        }

        // 获取上传进度。
        private static void streamProgressCallback(object sender, StreamTransferProgressArgs args)
        {
            System.Console.WriteLine($"ProgressCallback - Progress: {args.TransferredBytes * 100 / args.TotalBytes}%, TotalBytes:{args.TotalBytes}, TransferredBytes:{args.TransferredBytes}");
        }


        /// <summary>
        /// 断点续传上传方法
        /// </summary>
        /// <param name="BucketName"></param>
        /// <param name="objectName"></param>
        /// <param name="stream"></param>
        /// <param name="CheckpointDir"></param>
        /// <returns></returns>
        /// <exception cref="AggregateException"></exception>
        public async Task<bool> BreakPoint(string BucketName, string objectName, Stream stream, string CheckpointDir)
        {
            bool back = false;
            try
            {
                // 通过UploadFileRequest设置多个参数。
                UploadObjectRequest request = new UploadObjectRequest(BucketName, objectName, stream)
                {
                    // 指定上传的分片大小。
                    PartSize = 8 * 1024 * 1024,
                    // 指定并发线程数。
                    ParallelThreadCount = 10,
                    // checkpointDir保存断点续传的中间状态，用于失败后继续上传。
                    // 如果checkpointDir为null，断点续传功能不会生效，每次失败后都会重新上传。
                    CheckpointDir = CheckpointDir,
                    UploadStream = stream
                };
                // 断点续传上传。
                _ossClient.ResumableUploadObject(request);
                return true;
            }
            catch (OssException ex)
            {
                throw new AggregateException($"Failed with error code: {ex.ErrorCode}; Error info: {ex.Message}. \nRequestID:{ex.RequestId}\tHostID:{ex.HostId}");
            }
            catch (Exception ex)
            {
                throw new AggregateException(ex.Message);
            }
        }

        /// <summary>
        /// 压缩图片
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="targetSize">目标大小</param>
        /// <param name="orientation">旋转方向</param>
        /// <returns></returns>
        private Byte[] GetThumbnail(byte[] buffer, int targetSize, int orientation = 0)
        {
            using (var memoryStream = new MemoryStream(buffer))
            {
                if (targetSize == 0)
                {
                    return buffer;
                }

                using (Image image = new Bitmap(memoryStream))
                {
                    if (image.Width < targetSize)
                    {
                        return buffer;
                    }
                    //方向旋转
                    Rotation(image, image.Width, image.Height, 0);

                    //计算比例
                    var data = CalculateDimensions(image.Size, targetSize);

                    //开始压缩
                    System.Drawing.Image thumbImage = image.GetThumbnailImage(data.Width, data.Height, new Image.GetThumbnailImageAbort(ThumbnailCallback), System.IntPtr.Zero);

                    using MemoryStream backStream = new MemoryStream();
                    thumbImage.Save(backStream, ImageFormat.Jpeg);
                    //关闭缩略图对象
                    thumbImage.Dispose();

                    return backStream.GetBuffer();
                }
            }
        }


        private bool ThumbnailCallback()
        {
            return false;
        }

        /// <summary>
        /// 方向选择 旋转
        /// </summary>
        /// <param name="image"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="orientation"></param>
        private void Rotation(Image image, int width, int height, int orientation)
        {
            int ow = width;
            switch (orientation)
            {
                case 2:
                    image.RotateFlip(RotateFlipType.RotateNoneFlipX);
                    break;
                case 3:
                    image.RotateFlip(RotateFlipType.Rotate180FlipNone);
                    break;
                case 4:
                    image.RotateFlip(RotateFlipType.RotateNoneFlipY);
                    break;
                case 5:
                    image.RotateFlip(RotateFlipType.Rotate90FlipX);
                    break;
                case 6:
                    image.RotateFlip(RotateFlipType.Rotate90FlipNone);
                    width = height;
                    height = ow;
                    break;
                case 7:
                    image.RotateFlip(RotateFlipType.Rotate270FlipX);
                    break;
                case 8:
                    image.RotateFlip(RotateFlipType.Rotate270FlipNone);
                    width = height;
                    height = ow;
                    break;
                default:
                    break;
            }
        }


        private Size CalculateDimensions(Size oldSize, int targetSize, bool autoResize = false)
        {
            Size newSize = new Size();
            if (autoResize && oldSize.Height > oldSize.Width)
            {
                newSize.Width = (int)(oldSize.Width * ((float)targetSize / (float)oldSize.Height));
                newSize.Height = targetSize;
            }
            else
            {
                newSize.Width = targetSize;
                newSize.Height = (int)(oldSize.Height * ((float)targetSize / (float)oldSize.Width));
            }
            return newSize;
        }


        /// <summary>
        /// MD5加密
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        private string ComputeMd5Hash(MemoryStream stream)
        {
            string hash = "";
            using (MD5 md5 = MD5.Create())
            {
                stream.Position = 0;
                foreach (var item in md5.ComputeHash(stream))
                {
                    hash += item.ToString("x2");
                }
            }
            return hash;
        }


        /// <summary>
        /// 获取图片的宽高
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public async Task<Tuple<int, int>> GetImagesInfo(IFormFile file)
        {
            int width = 0;
            int height = 0;
            using (var memoryStream = new MemoryStream())
            {
                await file.CopyToAsync(memoryStream);
                using (Bitmap pic = new Bitmap(memoryStream))
                {
                    width = pic.Size.Width;
                    height = pic.Size.Height;
                }
            }
            return new Tuple<int, int>(width, height);
        }
        #endregion
    }
}
